#include "Skygen/StaticSkyboxGenerator.h"


#include <fstream>

namespace Orca {

	//--------------------------------------------------------------------------
	TStaticSkyboxGenerator::TStaticSkyboxGenerator() {

		skyClr			= vec3(0.8f, 0.8f, 1.0f);
		skyClr2			= vec3(0.4f, 0.6f, 1.0f);
		horzClr			= vec3(0.16f, 0.24f, 0.4f);
		cloudLightClr	= vec3(1.0f, 1.0f, 1.0f);
		cloudDarkClr	= vec3(0.256f, 0.384f, 0.64f);
		sunClr			= vec3(1.0f, 1.0f, 1.0f);

		using namespace std;

		ifstream ifs;
		ifs.open("Skygen_Static.txt");

		if(!ifs.fail()) {

			int r, g, b;
			char comment[255];

			ifs >> r >> g >> b;
			ifs.getline(comment, 255);
			skyClr = vec3(r / 255.0f, g / 255.0f, b / 255.0f);

			ifs >> r >> g >> b;
			ifs.getline(comment, 255);
			skyClr2 = vec3(r / 255.0f, g / 255.0f, b / 255.0f);

			ifs >> r >> g >> b;
			ifs.getline(comment, 255);
			horzClr = vec3(r / 255.0f, g / 255.0f, b / 255.0f);

			ifs >> r >> g >> b;
			ifs.getline(comment, 255);
			cloudLightClr = vec3(r / 255.0f, g / 255.0f, b / 255.0f);

			ifs >> r >> g >> b;
			ifs.getline(comment, 255);
			cloudDarkClr = vec3(r / 255.0f, g / 255.0f, b / 255.0f);

			ifs >> r >> g >> b;
			ifs.getline(comment, 255);
			sunClr = vec3(r / 255.0f, g / 255.0f, b / 255.0f);

			ifs.close();
		}
	}

	//--------------------------------------------------------------------------
	TStaticSkyboxGenerator::~TStaticSkyboxGenerator() {

	}

	//--------------------------------------------------------------------------
	Str TStaticSkyboxGenerator::getVertexProgramFilename() {

		return "Skygen/shader/Skybox_Static_vp.glsl";
	}

	//--------------------------------------------------------------------------
	Str TStaticSkyboxGenerator::getFragmentProgramFilename() {

		return "Skygen/shader/Skybox_Static_fp.glsl";
	}

	//--------------------------------------------------------------------------
	vec4 TStaticSkyboxGenerator::computeSkyboxColor(const vec3 &dir, int size) {

		vec3 sunDir(0.2f, 0.5f, 1.0f);
		normalize(sunDir);

		vec3 result;

		vec3 dir2 = dir;
		dir2[1] = - dir[1];

		// sun
		float sun = Math::clamp(dot(sunDir, dir2), 0.0f, 1.0f);
		lerp(result, sun, skyClr, sunClr);

		float height = Math::clamp(dir2[1] * 0.5f + 0.5f, 0.0f, 1.0f);
		height = 1.0f - Math::pow(1.0f-height, 3.0f);

		float h1 = Math::clamp( (height - 0.5f) * 2.0f, 0.0f, 1.0f);
		float h2 = Math::clamp(height * 2.0f, 0.0f, 1.0f);

		float noise = _getNoise(dir2);
		float noise_sat = Math::clamp(noise, 0.0f, 1.0f);

		float noise2 = _getNoise(vec3(dir2[0], -dir2[1], -dir2[2]) );
		float noise2_sat = Math::clamp(noise2, 0.0f, 1.0f);

		float shading = 1 - noise_sat;
		shading = Math::pow(shading, 8.0f);
		shading = Math::clamp(shading, 0.0f, 1.0f);

		float masking = noise * 2.0f + 0.2f;
		masking = Math::clamp(masking, 0.0f, 1.0f);

		// cloud rim light
		vec3 cloudClr;
		float cloudShading = 1 - ((1 - sun) * 0.5f + 0.5f);
		lerp(cloudClr, sun, cloudLightClr, cloudDarkClr);

		lerp(result, noise2_sat, result, cloudClr);
		lerp(result, noise_sat, result, cloudClr);
		lerp(result, noise_sat, result, cloudClr);

		result += sunClr * sun * 0.5f * masking * shading;

		// horizon
		lerp(result, h1, skyClr2, result);
		lerp(result, h2, horzClr, result);

#define SUN 1
#if SUN
		sun = Math::pow(sun, 64.0f);
		result += sunClr * ( sun * 0.25f );
#endif
		result[0] = Math::clamp(result[0], 0.0f, 1.0f);
		result[1] = Math::clamp(result[1], 0.0f, 1.0f);
		result[2] = Math::clamp(result[2], 0.0f, 1.0f);
		
		return toVec4(result, sun);
	}

	//--------------------------------------------------------------------------
	void TStaticSkyboxGenerator::updateShaderConstants(Shader *shader) {

	}

	//--------------------------------------------------------------------------
	void TStaticSkyboxGenerator::step(float dt) {

	}

	//--------------------------------------------------------------------------
	float TStaticSkyboxGenerator::_getNoise(const vec3 &dir) {

		float noise = 0.0f;
		
		float scale[] = {1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f, 256.0f};

		for(int i=0; i<8; ++i) {

			vec3 pos = dir * scale[i] * 3.0f;
			float n = mIPN.pnoise( pos[0], pos[1], pos[2] );

			noise += n * (1.0f / scale[i]);
		}

		return noise;
	}

	//--------------------------------------------------------------------------

}